/////////////////////////////////////////////////////////////////////////
//                                                                     //
//    MTGProject - A Magic The Gathering Networked Environment         //
//                                                                     //
//        Copyright (c) 2008 by The MTGProject Team (CSC230)           //
//                                                                     //
// This file is part of MTGProject.                                    //
//                                                                     //
// MTGProject is free software; you can redistribute it and/or         //
// modify it under the terms of the GNU Lesser General Public          //
// License as published by the Free Software Foundation; either        //
// version 3 of the License, or (at your option) any later version.    //
//                                                                     //
// This library is distributed in the hope that it will be useful,     //
// but WITHOUT ANY WARRANTY; without even the implied warranty of      //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       //
// GNU Lesser General Public License for more details.                 //
//                                                                     //
// You should have received a copy of the GNU Lesser General Public    //
// License along with this library; if not, write to the Free Software //
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA           //
// 02111-1307, USA, or contact the author(s):                          //
//                                                                     //
// Ruben Acuna <flyingfowlsoftware@earthlink.net>                      //
// Chris Watson <the5thdimension@hotmail.com>                          //
//                                                                     //
/////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;

namespace MTGProject
{
    class CardIO
    {
        public const string cardPath = "cards\\";
        public const string deckPath = "decks\\";

        public static void SaveDeckArray(string filename, string[] deck)
        {
            //create linked list from deck array
            LinkedList<string> deckList = new LinkedList<string>();
            foreach (string card in deck)
                deckList.AddFirst(card);

            if(File.Exists(filename))
                File.Delete(filename);

            XmlTextWriter xmlWriter = new XmlTextWriter(filename, System.Text.Encoding.UTF8);
            xmlWriter.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
            xmlWriter.Formatting = Formatting.Indented;
            xmlWriter.WriteStartElement("MTGCard");
            xmlWriter.Close();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(filename);
            XmlNode root = xmlDoc.DocumentElement;

            XmlElement deckNode = xmlDoc.CreateElement("deck");
            root.AppendChild(deckNode);
            root = deckNode;

            XmlElement cardsNode = xmlDoc.CreateElement("cards");
            root.AppendChild(cardsNode);
            root = cardsNode;

            while (deckList.Count > 0)
            {
                string card = deckList.First.Value;
                int cardCount = 0;

                while (deckList.Contains(card))
                {
                    deckList.Remove(card);
                    cardCount++;
                }

                XmlElement newNode = xmlDoc.CreateElement("card");
                newNode.SetAttribute("count", cardCount.ToString());
                newNode.AppendChild(xmlDoc.CreateTextNode(card));

                root.AppendChild(newNode);
            }

            xmlDoc.Save(filename);
        }

        public static string[] LoadDeckArray(string filename)
        {
            string[] deckArray;
            LinkedList<string> deckLinkedList = new LinkedList<string>();

            //filename = deckPath + filename;

            if (File.Exists(filename))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(filename);

                XmlNodeList deckList = doc.GetElementsByTagName("deck");

                //HACK: each xml should only contain one entry so this will run once.
                foreach (XmlNode node in deckList)
                {
                    XmlElement deckElement = (XmlElement)node;

                    //load cards
                    XmlElement cardsElement = (XmlElement)deckElement.GetElementsByTagName("cards")[0];
                    XmlNodeList cardsList = cardsElement.GetElementsByTagName("card");

                    foreach (XmlNode cardNode in cardsList)
                    {
                        string cardCount = cardNode.Attributes.GetNamedItem("count").InnerText.ToLower();

                        for (int card = 0; card < int.Parse(cardCount); card++)
                        {
                            deckLinkedList.AddLast(cardNode.InnerText);
                        }
                    }
                }
            }
            else
            {
                //File.Create(filename);
                //throw new Exception(Directory.GetCurrentDirectory());
                throw new Exception("file did not exist");
            }


            deckArray = new string[deckLinkedList.Count];
            deckLinkedList.CopyTo(deckArray, 0);

            return deckArray;
        }

		public static CardPool LoadXMLDeck(string filename)
		{
            filename = deckPath + filename;

			CardPool deck = new CardPool(Zone.DECK);
            string[] deckArray = LoadDeckArray(filename);

            foreach(string card in deckArray)
                deck.AddCard(LoadXMLCard(card, true));

            return deck;
        }
		
        public static ICard LoadXMLCard(string filename, bool mount)
        {
            if(mount)
                filename = cardPath + filename;

            ICard card = null;

            if (File.Exists(filename))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(filename);

                XmlNodeList fileMoveList = doc.GetElementsByTagName("card");
                
                //HACK: each xml should only contain one entry so this will run once.
                foreach (XmlNode node in fileMoveList)
                {
                    XmlElement cardElement = (XmlElement)node;

                    //ICard values
                    string cardName = cardElement.GetElementsByTagName("name")[0].InnerText;
                    string script = cardElement.GetElementsByTagName("script")[0].InnerText;
                    string imageURL = cardElement.GetElementsByTagName("imageurl")[0].InnerText;

                    LinkedList<ManaColor> cardColors = new LinkedList<ManaColor>();
                    LinkedList<ManaColor> cardCost = new LinkedList<ManaColor>();
                    
                    //load colors from enumerations.
                    XmlElement colorsElement = (XmlElement)cardElement.GetElementsByTagName("colors")[0];
                    XmlNodeList colorsList = colorsElement.GetElementsByTagName("color");
                    for (int color = 0; color < colorsList.Count; color++)
                    {
                        string colorEnumName = colorsList[color].InnerText;

                        for (int colorEnumIndex = 0; colorEnumIndex < Settings.CARD_COLOR_COUNT; colorEnumIndex++)
                            if (((ManaColor)colorEnumIndex).ToString().ToLower().Equals(colorEnumName.ToLower()))
                                cardColors.AddLast((ManaColor)colorEnumIndex);
                    }

                    //load cost from enumerations.
                    XmlElement costElement = (XmlElement)cardElement.GetElementsByTagName("cost")[0];
                    XmlNodeList costList = costElement.GetElementsByTagName("color");
                    for (int color = 0; color < costList.Count; color++)
                    {
                        string colorEnumName = costList[color].InnerText;

                        for (int colorEnumIndex = 0; colorEnumIndex < Settings.CARD_COLOR_COUNT; colorEnumIndex++)
                            if (((ManaColor)colorEnumIndex).ToString().ToLower().Equals(colorEnumName.ToLower()))
                                cardCost.AddLast((ManaColor)colorEnumIndex);
                    }

                    //Card class specific values
                    string cardType = node.Attributes.GetNamedItem("class").InnerText.ToLower();

                    if (cardType.Equals("cardland"))
                    {
                        card = new CardLand(cardName, cardColors, cardCost, script, imageURL, null);
                        
                    }
                    else if (cardType.Equals("cardsorcery"))
                    {
                        card = new CardSorcery(cardName, cardColors, cardCost, script, imageURL, null);
                    }
                    else if (cardType.Equals("cardenchantment"))
                    {
                        string enchantmentType = cardElement.GetElementsByTagName("type")[0].InnerText;
                        EnchantmentType type = EnchantmentType.GLOBAL; //temporary value

                        for (int typeEnumIndex = 0; typeEnumIndex < Settings.ENCHANTMENT_TYPE_COUNT; typeEnumIndex++)
                            if (((EnchantmentType)typeEnumIndex).ToString().ToLower().Equals(enchantmentType.ToLower()))
                            {
                                type = (EnchantmentType)typeEnumIndex;
                                break;
                            }

                        card = new CardEnchantment(cardName, cardColors, cardCost, type, script, imageURL, null);
                    }
                    else if (cardType.Equals("cardcreature"))
                    {
                        int power = int.Parse(cardElement.GetElementsByTagName("power")[0].InnerText);
                        int toughness = int.Parse(cardElement.GetElementsByTagName("toughness")[0].InnerText);
                        LinkedList<CreatureKeyword> cardKeywords = new LinkedList<CreatureKeyword>();
                        LinkedList<CreatureType> cardTypes = new LinkedList<CreatureType>();

                        //keywords from enumerations
                        XmlElement keywords = (XmlElement)cardElement.GetElementsByTagName("keywords")[0];
                        XmlNodeList keywordsList = keywords.GetElementsByTagName("keyword");
                        for (int keyword = 0; keyword < keywordsList.Count; keyword++)
                        {
                            string keywordEnumName = keywordsList[keyword].InnerText;

                            for (int keywordEnumIndex = 0; keywordEnumIndex < Settings.CARD_CREATURE_KEYWORDS; keywordEnumIndex++)
                                if (((CreatureKeyword)keywordEnumIndex).ToString().ToLower().Equals(keywordEnumName.ToLower()))
                                    cardKeywords.AddLast((CreatureKeyword)keywordEnumIndex);
                        }

                        //types from enumerations.
                        XmlElement typesElement = (XmlElement)cardElement.GetElementsByTagName("types")[0];
                        XmlNodeList typesList = typesElement.GetElementsByTagName("type");
                        for (int color = 0; color < typesList.Count; color++)
                        {
                            string colorEnumName = typesList[color].InnerText;

                            for (int colorEnumIndex = 0; colorEnumIndex < Settings.CARD_TYPE_COUNT; colorEnumIndex++)
                                if (((CreatureType)colorEnumIndex).ToString().ToLower().Equals(colorEnumName.ToLower()))
                                    cardTypes.AddLast((CreatureType)colorEnumIndex);
                        }

                        card = new CardCreature(cardName, cardColors, cardCost, cardTypes, cardKeywords, power, toughness, script, imageURL, null);
                    }
                    else
                        throw new Exception("CardIO: Attempted load unknown card type.");
                }
            }

            return card;
        }

        public static void WriteXMLCard(ICard card, string filename)
        {
            if (!(card is CardCreature) && !(card is CardSorcery))
                throw new Exception("Attempted to write unknown card type in WriteXMLCard()");

            //instantiate XmlWriterSettings and enable indenting
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;

            //instantiate XmlWriter and create XML file in windows
            XmlWriter file = XmlWriter.Create(filename, settings);

            file.WriteStartDocument();

            //start MTGCard element
            file.WriteStartElement("MTGCard");
            file.WriteStartElement("card");

            if (card is CardCreature)
                file.WriteAttributeString("class", "CardCreature");
            else if (card is CardSorcery)
                file.WriteAttributeString("class", "CardSorcery");

            //name
            file.WriteElementString("name", card.Name);

            //colors
            file.WriteStartElement("colors");
            foreach (ManaColor color in card.Colors)
                file.WriteElementString("color", color.ToString());
            file.WriteEndElement();

            //cost
            file.WriteStartElement("cost");
            foreach (ManaColor cost in card.Cost)
                file.WriteElementString("color", cost.ToString());
            file.WriteEndElement();

            if (card is CardCreature)
            {
                //types
                file.WriteStartElement("types");
                foreach (CreatureType type in ((CardCreature)card).Types)
                    file.WriteElementString("type", type.ToString());
                file.WriteEndElement();

                //keywords
                file.WriteStartElement("keywords");
                foreach (CreatureKeyword keywords in ((CardCreature)card).Keywords)
                    file.WriteElementString("keyword", keywords.ToString());
                file.WriteEndElement();

                //power
                file.WriteElementString("power", ((CardCreature)card).Power.ToString());

                //toughness
                file.WriteElementString("toughness", ((CardCreature)card).Toughness.ToString());
            }

            //script
            file.WriteElementString("script", card.Script);

            //image url
            file.WriteElementString("imageurl", card.ImageURL);

            //end card element
            file.WriteEndElement();

            //end MTGCard element
            file.WriteEndDocument();
            file.Flush();
            file.Close();
        }

        //TODO: make more generic so that you can check any card type
        public static bool IsXMLCardLand(string filename)
        {
            //HACK: we should be checking inside of the file.
            return (filename.Contains("plains") || filename.Contains("island") || filename.Contains("swamp") || filename.Contains("mountain") || filename.Contains("forest"));
        }

        /// <summary>
        /// Compares each instance variable of two CardCreatures.
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        public static Boolean isEqual(CardCreature one, CardCreature two)
        {
            if (!(elementsEqual<CreatureType>(one.Types, two.Types) ))
                return false;
            
            if (!(elementsEqual(one.Cost, two.Colors) ))
                return false;
            
            if (!(elementsEqual(one.Cost, two.Cost) ))
                return false;

            if (!one.Script.ToLower().Equals(two.Script.ToLower() ))
                return false;

            if (!one.Name.ToLower().Equals(two.Name.ToLower() ))
                return false;
            
            if (!(one.Toughness == two.Toughness))
                return false;
            
            if (!(one.Power == two.Power))
                return false;
            
            return true;
        }

        public static Boolean elementsEqual<T>(LinkedList<T> one, LinkedList<T> two)
        {
            LinkedList<T> tempOne = new LinkedList<T>();

            foreach (T element in one)
                tempOne.AddLast(element);


            foreach (T element in two)
                tempOne.Remove(element);

            if ((tempOne.Count == two.Count) && tempOne.Count != 0)
                return false;

            return true;
        }
    }
}
